# -*- coding: utf-8 -*-
# @Time    : 2023/2/7 16:25
# @Author  : kali
from importlib import import_module

from flask import Response
from flask_restful import Resource, Api
from setuptools import find_packages
from werkzeug.utils import find_modules

from utils import get_dict, read_chunk, get_snake_case


class ApiResource(Resource):
    """
    api接口抽象基类
    """
    __sub_url__ = None
    __sub_url_list__ = None
    __url__ = None
    __url_list__ = None
    __permission_register_list__ = None
    __get_args_schema__ = None
    __get_json_schema__ = None
    __post_args_schema__ = None
    __post_json_schema__ = None
    __put_json_schema__ = None
    __put_args_schema__ = None
    __delete_args_schema__ = None
    __delete_json_schema__ = None
    __get_request_sample__ = None
    __post_request_sample__ = None
    __put_request_sample__ = None
    __delete_request_sample__ = None
    __get_response_sample__ = None
    __post_response_sample__ = None
    __put_response_sample__ = None
    __delete_response_sample__ = None
    __error_code_list__ = None

    @classmethod
    def success(cls, code=200, *args, **kwargs) -> dict:
        result = {
            "result": "success",
            "code": code
        }
        result.update(get_dict(*args, **kwargs))
        return result

    @classmethod
    def failure(cls, code=400, reason=None, *args, **kwargs) -> dict:
        result = {
            "result": "failure",
            "code": code,
            "reason": reason
        }
        result.update(get_dict(*args, **kwargs))
        return result

    @classmethod
    def send_file(cls, file_name):
        return Response(read_chunk(file_name), content_type='application/octet-stream')


def _default_is_alive():
    return 'yes'


def module_exists(module_name):
    try:
        __import__(module_name)
        return True
    except:
        return False


def find_all(package_name):
    if not module_exists(package_name):
        return
    try:
        for module_name in find_modules(package_name, recursive=True):
            yield import_module(module_name)
    except:
        return


def get_all(package_name, class_type):
    for module in find_all(package_name):
        for attr in dir(module):
            if attr.startswith('__'):
                continue
            value = getattr(module, attr)
            if not isinstance(value, type):
                continue
            if issubclass(value, class_type) and value != class_type:
                yield value


def travel_resources():
    if not module_exists('api'):
        return None, None

    for version in find_packages('api'):
        if '.' in version or not version.startswith('v'):
            continue
        for resource in get_all('api.' + version, ApiResource):
            yield resource, version


def get_url_list(value, version):
    if value.__url__:
        return [value.__url__]
    if value.__url_list__:
        return value.__url_list__

    prefix = f"/stat/overflow/api/{version}/"
    if value.__sub_url__:
        return [prefix + value.__sub_url__]
    if value.__sub_url_list__:
        return [prefix + sub_url for sub_url in value.__sub_url_list__]
    return [prefix + get_snake_case(value.__name__)]


def _load_route():
    route_list = []
    for resource, version in travel_resources():
        if not resource or not version:
            continue

        endpoint = resource.__name__.lower() + version
        url_list = tuple(get_url_list(resource, version))
        if not url_list:
            continue
        route_list.append((resource, url_list, {'endpoint': endpoint}))
    return route_list


def auto_load_api(app):
    """
    自动加载接口
    :param app:
    :return:
    """

    # 服务器接口服务测试接口
    prefix = "/stat/overflow/api"
    alive_api = f"{prefix}/v1/alive"
    app.route(alive_api)(_default_is_alive)

    # 加载并注册系统中的接口
    api = Api(app)
    route_list = _load_route()
    for resource, routes, kwargs in route_list:
        if 'strict_slashes' not in kwargs:
            kwargs['strict_slashes'] = False
        api.add_resource(resource, *routes, **kwargs)

    print(alive_api)
    for item in route_list:
        print(item)
